/* Copyright 2014 Jan Wolter */

#include "stack.h"
#include "tableau.h"
#include "found.h"
#include "waste.h"
#include "stock.h"
#include "solve.h"
#include "game.h"

char *gamename= "Storehouse";
char *gameopt= "";
Stack *gathered= NULL;
Stack *temp= NULL;

int reservei;

int nplayed= 0;
int nwon= 0;
int nabandoned= 0;
int cardsleft= 0;
int maxoff;
int baserank;

void setopt(char c) {}

/* Given the pip value of a card, what is it's foundation rank, where the
 * basecard's baserank is 0, the next card is 1, and so on.
 */
int basepip(int n)
{
    return (n - baserank + npip)%npip;
}


/* This is the main program for setting up the current game */
void exec(int gather, int arif, int over, int cut)
{
    /* Could set things like ndeck, nsuit here */

    /* Build and shuffle the deck */
    Stack *shuf= mixcards(arif, over, cut);

    /* In storehouse, the base card rank is always 2 */
    baserank= 2;

    /* Create the foundations */
    superfound= sf_constbase;	/* Must be configured before calling makeFound */
    for (int s= 0; s < nsuit; s++)
	makeFound(baserank, s, +1, 13);

    /* Find all 2's, and put them on the foundation */
    for (int s= 0; s < nsuit; s++)
    {
	Card *c= extractCard(shuf, baserank, -1);
	pushStack(stacks+foundation[nfound-nsuit+c->s].id, c);
    }

    /* Create the reserve - part of the tableau really */
    makeTableau(13, FLIP_ALL, shuf, S_NONE, 0, S_NONE, 0, 0, F_NONE, BREAK_ANYWHERE, 0);
    reservei= ntableau-1;

    /* Create the tableau */
    for (int i= 1; i <= 4; i++)
	makeTableau(1, FLIP_NONE, shuf,
	    S_SAMESUIT, -1, S_SAMESUIT, -1, baserank, F_ANYTHING, 1, reservei+1);

    /* Create the waste */
    makeWaste();

    /* Create the stock */
    makeStock(DT_WASTE, 1, 3, shuf);

    freeStack(shuf);

    /* Print initial state */
    if (verbose > 1) printState(stdout, stacks);

    maxoff= 0;
    int win= solve(gather,0);

    if (verbose > 0) printf("maxoff=%d\n",maxoff);
    nplayed++;
    cardsleft+= maxoff;
    if (win == 1)
	nwon++;
    else if (win == 2)
	nabandoned++;

    cleanFound();
    cleanTableau();
    cleanWaste();
    cleanStock();
    cleanStacks();
    cleanVisited();
}

/* Gather up the cards into the global 'gathered' stack. This has to be
 * non-destructive, since we may not be done solving when it is called.
 */
void gatherAll()
{
    if (gathered == NULL)
	gathered= allocStack(ncard);
    else
	gathered->n= 0;

    gatherFound(gathered);
    gatherTableau(gathered);
    gatherStock(gathered);
    gatherWaste(gathered, 0);
    /* Don't need to gather stock, because it's always empty when we are done */
    if (gathered->n != ncard)
    {
	printf("Gather Failed - found %d cards\n",gathered->n);
	exit(1);
    }
}

/* How good is a move?
 *
 * Returns 0 if the move is completely safe or manditory. Such moves will not
 * be marked as choice points for backtracking.
 *
 * For other moves, positive values are returned with lower values for better
 * moves.
 *
 * Negative values mean the move should not be done at all.
 *
 * So for Storehouse:
 *
 *   0 = Any move to the foundation or to a non-empty tableau space.
 *       Any move from the reserve.
 *   1 = deal a card from the stock to the waste.
 *   2 = moves from waste to empty tableau space.
 *
 * We never move a card from the tableau to an empty tableau space.
 */
int rateMove(Stack *src, int index1, int len, Stack *dst)
{
    Card *mc= src->c[index1];

    /* Always make any moves from the reserve is Great */
    if (src->type == TABLEAU && src->tid == reservei)
	return 0;

    /* Deal a card from the stock to the tableau */
    if (src->type == STOCK) return 1;

    /* Moving to empty tableau stacks is the only time life gets complex */
    if (dst->type == TABLEAU && dst->n == 0)
    {
	if (src->type == WASTE) return 2;
	if (src->type == TABLEAU) return -1;
    }

    /* Everything else is peachy */
    return 0;
}

void printState(FILE *f, Stack *stks)
{
    printFound(f, stks);
    printTableau(f, stks);
    printStock(f, stks);
    printWaste(f, stks);
}

int victory()
{
    int off= nOff();
    if (off > maxoff) maxoff= off;
    return (off == ncard);
}

/* Return a quality rating for the current solution. Bigger is better. Usually
 * it's just cards off. This is used to decide which solution to report in
 * cases where we never win.
 */
int quality()
{
    return nOff();
}

void summary()
{
    printf("Played:    %d games\n",nplayed);
    printf("Won:       %d games (%.2f%)\n",nwon,(float)nwon*100/nplayed);
    printf("Abandoned: %d games (%.2f%)\n",nabandoned,(float)nabandoned*100/nplayed);
    printf("Cards Off: %.2f\n",(float)cardsleft/nplayed);
}

/* Return a string that uniquely describes the current state. */
char *currstate()
{
    /* ncards for all the cards on things, 4 for tableaus, 1 for reserve,
     * 4 for list terminators.
     */
    char buf[ncard+20], *p= buf;

    /* Numbers of cards in each foundation pile */
    /* We add one to each count because we don't want any zeros */
    for (int k= 0; k < nfound; k++)
	*(p++)= stacks[foundation[k].id].n + 1;

    /* Count of cards on the reserve */
    *(p++)= stacks[tableau[reservei].id].n + 1;

    /* List of cards on each tableau pile - probably we should normalize
     * the order of tableau piles, sorting them by the first card on them,
     * but that is unlikely to be a bit deal in Canfield. Also the flippedness
     * of cards may need to matter, but again, that doesn't matter in Canfield.
     */
    for (int k= 0; k < ntableau; k++)
    {
	if (k == reservei) continue;
	Stack *s= stacks + tableau[k].id;
	for (int i= 0; i < s->n; i++)
	    *(p++)= (s->c[i] - deck) + 1;
	*(p++)= ncard + 1; /* The card list terminator */
    }

    /* List of cards on stock/waste */

    /* We don't need this. For any given deal, the other state variables
     * give us enough information to tell what cards are in the bouquet, and
     * their order never changes, so this is redundant.
    Stack *bs= stacks + bouquet[0].id;
    for (int i= 0; i < bs->n; i++)
	*(p++)= (bs->c[i] - deck) + 1;
    */

    /* Instead, we just want the number of cards in the stock, so our state
     * changes as we deal cards.  We don't want the pass number.
     */
    *(p++)= stacks[stock[0].id].n;

    *p= '\0';

    return strdup(buf);
}
